package ufabc.edu.va.agenteminhoca;

import sim.engine.SimState;
import sim.engine.Steppable;
import sim.engine.Stoppable;
import sim.field.grid.SparseGrid2D;
import sim.util.Bag;
import sim.util.Int2D;
import sim.util.IntBag;

@SuppressWarnings("serial")
public class Minhoca implements Steppable {
	private Minhocas minhocas;
	Chromosome gene = new Chromosome();
	private int score;
	private int facing;
	private int currentState;
	public Stoppable stoppable;

	public int getScore() {
		return score;
	}

	public Minhoca() {
		this.score = 1;
		this.gene.randomize();
		this.facing = 0;
		this.currentState = 0;
	}
	
	public Minhoca(Chromosome gene) {
		this.score = 1;
		this.gene.copy(gene);
		this.facing = 0;
		this.currentState = 0;
	}

	public void mutate(double prob) {  // mutate each item in this Eater's gene with given probability
		if (prob > 0) {
			this.gene.mutate(prob);
		}
	}	

	
	@Override
	public void step(SimState state) {
		minhocas = (Minhocas) state;
		Int2D location = minhocas.worldMinhocas.getObjectLocation(this);
		
		int newX = location.x;
		int newY = location.y;

		switch (this.facing) {
		case 0: 
			newX += 1;
			break;
		case 1: 
			newY -= 1;
			break;
		case 2: 
			newX -= 1;
			break;
		case 3: 
			newY += 1;
			break;
		}
		        
		int conteudoPosicao = Chromosome.seeSpace;

		if (verifyFood(minhocas.worldComidas, location))
			conteudoPosicao = Chromosome.seePlant;
		else if (getWall(minhocas, location))
			conteudoPosicao = Chromosome.seeWall;
		else if (seeEaterAround(minhocas.worldMinhocas, location)) {
			conteudoPosicao = Chromosome.seeEater;			
		}
		
		int estadoAtual = this.currentState;

		byte newState = this.gene.getNewState(estadoAtual, conteudoPosicao);
		byte whatToDo = this.gene.getWhatToDo(estadoAtual, conteudoPosicao);
		this.currentState = newState;
		boolean isMoving = true;
		
		switch (whatToDo) {
		case 0: 
			facing--;
			if (facing < 0)
				facing = 3;
			break;
		case 1:
			facing++;
			if (facing > 3)
				facing = 0;
			break;
		case 2:
			if (conteudoPosicao > 0) 
				isMoving = false;
			break;
		case 3: {
			int col = location.x;
			int row = location.y;
			switch (this.facing) {
			case 0: 
				col = col - 1;
				break;
			case 1: 
				row = row + 1;
				break;
			case 2: 
				col = col + 1;
				break;
			case 3: 
				row = row - 1;
				break;
			}
			newX = row;
			newY = col;
			if (conteudoPosicao > 0) 
				isMoving = false;
		}
		break;
		}
		if (isMoving) {
			Int2D newLocation = new Int2D(newX, newY);			
			verifyFood(minhocas.worldComidas, newLocation);
			minhocas.worldMinhocas.setObjectLocation(this, newLocation);
		}
	}

	private boolean seeEaterAround(SparseGrid2D worldMinhocas, Int2D location) {
		
		Bag result = new Bag();
		IntBag xPos = new IntBag();
		IntBag yPos = new IntBag();
		result = worldMinhocas.getNeighborsMaxDistance(location.x, location.y, 1, false, result, xPos, yPos);
		
		if (result.isEmpty()) {
			return false;
		}
		return true;
	}

	private boolean verifyFood(SparseGrid2D comidas, Int2D location) {
//		Bag eaten = comidas.getObjectsAtLocation(location);
		Bag eaten = new Bag();
		IntBag xPos = new IntBag();
		IntBag yPos = new IntBag();
		eaten = comidas.getNeighborsMaxDistance(location.x, location.y, 1, false, eaten, xPos, yPos);
		
		if (eaten != null) {
			for (int i = 0; i < eaten.size(); i++) {
				comidas.remove(eaten.get(i));
				this.score += 1;
			}
			return true;
		}

		return false;
	}

	private boolean getWall(Minhocas minhocas, Int2D location) {
		return location.x < 0 && location.x > minhocas.getGridWidth() && location.y < 0 && location.y > minhocas.getGridHeight();
	}
	
	public void stop()
	{
		stoppable.stop();
	}
}